package main

import (
	"fmt"
	"sync"
	"time"
)

func main() {
	mutex := sync.Mutex{}
	cond := sync.NewCond(&mutex)
	var wg sync.WaitGroup
	for i := 1; i <= 6; i++ {
		wg.Add(1)
		go func(num int) {
			defer wg.Done()
			fmt.Println(num, "号运动员就位")
			cond.L.Lock()         // 获取锁
			defer cond.L.Unlock() // 释放锁
			cond.Wait()           // 阻塞，等待通知
			fmt.Println(num, "号开始跑...")
		}(i)
	}
	// 等2秒，所有运动员就位
	time.Sleep(time.Second * 2)
	wg.Add(1)
	go func() {
		defer wg.Done()
		fmt.Println("各就各位...")
		time.Sleep(time.Millisecond * 100)
		fmt.Println("预备...")
		time.Sleep(time.Millisecond * 100)
		fmt.Println("PONG !!!")
		//cond.Broadcast()
		for i := 1; i <= 6; i++ {
			fmt.Println("通知", i)
			cond.Signal()
			time.Sleep(time.Millisecond * 100)
		}
	}()
	wg.Wait()

	fmt.Println("示例二")

	type Mailbox struct {
		taskNum int
		msg string
		readFinished bool
	}
	mailbox := Mailbox{readFinished: true}  // 这个是共享变量，操作需要加锁

	var lock sync.RWMutex
	sendCond := sync.NewCond(&lock)
	recvCond := sync.NewCond(lock.RLocker())

	// 开启10个消费者
	for i := 0; i < 10; i++  {
		go func(i int) {
			for {
				// 加读锁
				lock.RLock()
				// 未读过，并且取模是自己的序号才符合条件
				// 条件取反，就是不符合条件，这个就是需要等待唤醒的逻辑
				for !(!mailbox.readFinished && mailbox.taskNum % 10 == i) {
					recvCond.Wait()
				}
				fmt.Printf("消费者%d: %s\n", i, mailbox.msg)
				mailbox.readFinished = true  // 这里修改值了，是否合适？
				lock.RUnlock()
				sendCond.Signal()
			}
		}(i)
	}
	// 主线程作为生产者
	for j := 0; j < 100; j ++{
		if j != 0 {
			// 第一次不需要等待，之后每次都停顿一下
			time.Sleep(time.Millisecond * 300)
		}
		lock.Lock()
		// 读取完毕可以继续操作
		for !mailbox.readFinished {
			sendCond.Wait()
		}
		mailbox.taskNum = j + 1
		mailbox.msg = fmt.Sprintf("%03d Message", j+1)
		mailbox.readFinished = false
		fmt.Printf("生产者 : %03d\n", j+1)
		lock.Unlock()
		recvCond.Broadcast()
	}
	// 还要等待最后一个任务被执行完
	lock.Lock()
	sendCond.Wait()  // 不要漏了上面和下面的加锁、解锁操作
	lock.Unlock()
}
